package com.zh.common.plugins.shiro;

import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.zh.common.domain.MenuDO;
import com.zh.common.domain.RoleDO;
import com.zh.common.domain.UserDO;
import com.zh.common.plugins.shiro.JwtToken.JWTToken;
import com.zh.common.plugins.shiro.utils.JWTUtil;
import com.zh.common.service.MenuService;
import com.zh.common.service.RoleService;
import com.zh.common.service.UserService;
import com.zh.common.utils.CheckEmptyUtil;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 *
 * @ProjectName:
 * @Package:        com.zh.common.plugins.shiro
 * @ClassName:      MyRealm
 * @Description:    自定义Realm
 * @Author:         lds
 * @CreateDate:     2019/5/22 0022 20:01
 * @UpdateUser:     更新者
 * @UpdateDate:     2019/5/22 0022 20:01
 * @UpdateRemark:   更新内容
 * @Version:        1.0
 */
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Autowired
    RedisTemplate redisTemplate;
    
    @Autowired
    TokeService tokeService;

    @Autowired
    MenuService menuService;

    @Autowired
    RoleService roleService;

    /**
     * 大坑！，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 授权
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = JWTUtil.getUsername(principals.toString());
        int userType = JWTUtil.getUserType(principals.toString());
        SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
        try {
            List<RoleDO> roleList = roleService.getRolesByUserName(userName, userType);
            Set<String> roleSet = roleList.stream().map(role -> role.getRoleSign()).collect(Collectors.toSet());

            List<MenuDO> menuList = menuService.getMenuByUserName(userName, userType);
            Set<String> menuSet=new HashSet<>();
            menuList.forEach(menuDO->{
                if (CheckEmptyUtil.isNotEmpty(menuDO.getPerms())) {
                    menuSet.add(menuDO.getPerms());
                }
            });

            auth.setRoles(roleSet);
            auth.setStringPermissions(menuSet);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return auth;
    }

    /**
     * 认证
     *
     * @param auth
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        // 解密获得userName，用于和数据库进行对比
        String userName = JWTUtil.getUsername(token);
        int userType = JWTUtil.getUserType(token);
        UserDO vo = this.userService.getByUserName(userName,userType);
        String redisUserInfo = tokeService.getTokenFromRedis(userName, userType);

        Map result = JWTUtil.verify(token, userName, vo.getPassword(),userType);
        Exception exception = (Exception) result.get("exception");

        if (vo == null) {
            throw new UnknownAccountException("该帐号不存在！");
        } else if (vo.getStatus() == 0) {
            throw new UnknownAccountException("该帐号已被禁用！");
        } else if (exception != null && exception instanceof SignatureVerificationException) {
            throw new AuthenticationException("Token错误(Token incorrect.)！");
        } else if (exception != null && exception instanceof TokenExpiredException) {
            throw new AuthenticationException("Token已过期(Token expired.)！");
            //被T
        } else if(StringUtils.isEmpty(redisUserInfo)){
            throw new AuthenticationException("Token已失效(Token invalid.)！");
        }else {
            AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(token, token, vo.getUsername());
            return authcInfo;
        }
    }
}
